﻿// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Azure.Core.TestFramework;
using NUnit.Framework;

namespace Azure.AI.DocumentIntelligence.Tests
{
    public class MiscellaneousOperationsLiveTests : DocumentIntelligenceLiveTestBase
    {
        public MiscellaneousOperationsLiveTests(bool isAsync)
            : base(isAsync)
        {
        }

        #region Resource Details

        [RecordedTest]
        public async Task GetResourceDetails()
        {
            var client = CreateDocumentIntelligenceAdministrationClient();
            var startTime = Recording.UtcNow;

            // Ensure we will have at least one custom model to populate the resource details.
            await using var disposableModel = await BuildDisposableDocumentModelAsync(TestEnvironment.BlobContainerSasUrl);

            DocumentIntelligenceResourceDetails resourceDetails = await client.GetResourceDetailsAsync();

            Assert.That(resourceDetails.CustomDocumentModels, Is.Not.Null);
            Assert.That(resourceDetails.CustomDocumentModels.Count, Is.GreaterThan(0));
            Assert.That(resourceDetails.CustomDocumentModels.Limit, Is.GreaterThanOrEqualTo(resourceDetails.CustomDocumentModels.Count));
        }

        #endregion Resource Info

        #region Get Operation

        [RecordedTest]
        public async Task GetOperationWithDocumentModelBuild()
        {
            var client = CreateDocumentIntelligenceAdministrationClient();
            var description = "This model was generated by a .NET test.";
            var tags = new Dictionary<string, string>() { { "tag1", "value1" }, { "tag2", "value2" } };
            var startTime = Recording.UtcNow;

            await using var disposableModel = await BuildDisposableDocumentModelAsync(TestEnvironment.BlobContainerSasUrl, description, tags);

            DocumentIntelligenceOperationDetails operationDetails = await client.GetOperationAsync(disposableModel.Operation.Id);

            // The endpoint environment variable may or may not contain a trailing '/' character. Trim the string
            // to ensure the behavior is consistent.

            var trimmedEndpoint = TestEnvironment.Endpoint.Trim('/');
            var resourceLocation = $"{trimmedEndpoint}/documentintelligence/documentModels/{disposableModel.ModelId}?api-version={ServiceVersionString}";

            ValidateOperationDetails(operationDetails, disposableModel.Operation.Id, resourceLocation, startTime, tags);

            var buildOperationDetails = operationDetails as DocumentModelBuildOperationDetails;

            Assert.That(buildOperationDetails, Is.Not.Null);

            DocumentAssert.AreEqual(disposableModel.Value, buildOperationDetails.Result);
        }

        [RecordedTest]
        public async Task GetOperationWithDocumentClassifierBuild()
        {
            var client = CreateDocumentIntelligenceAdministrationClient();
            var description = "This model was generated by a .NET test.";
            var tags = new Dictionary<string, string>();
            var startTime = Recording.UtcNow;

            await using var disposableClassifier = await BuildDisposableDocumentClassifierAsync(description);

            DocumentIntelligenceOperationDetails operationDetails = await client.GetOperationAsync(disposableClassifier.Operation.Id);

            // The endpoint environment variable may or may not contain a trailing '/' character. Trim the string
            // to ensure the behavior is consistent.

            var trimmedEndpoint = TestEnvironment.Endpoint.Trim('/');
            var resourceLocation = $"{trimmedEndpoint}/documentintelligence/documentClassifiers/{disposableClassifier.ClassifierId}?api-version={ServiceVersionString}";

            ValidateOperationDetails(operationDetails, disposableClassifier.Operation.Id, resourceLocation, startTime, tags);

            var buildOperationDetails = operationDetails as DocumentClassifierBuildOperationDetails;

            Assert.That(buildOperationDetails, Is.Not.Null);

            DocumentAssert.AreEqual(disposableClassifier.Value, buildOperationDetails.Result);
        }

        [RecordedTest]
        public void GetOperationCanParseError()
        {
            var client = CreateDocumentIntelligenceAdministrationClient();
            var operationId = "00000000000_00000000-0000-0000-0000-000000000000";

            RequestFailedException ex = Assert.ThrowsAsync<RequestFailedException>(() => client.GetOperationAsync(operationId));

            Assert.That(ex.ErrorCode, Is.EqualTo("NotFound"));
        }

        #endregion Get Operation

        #region List Operations

        [RecordedTest]
        public async Task GetOperations()
        {
            var client = CreateDocumentIntelligenceAdministrationClient();
            var description = "This model was generated by a .NET test.";
            var tags = new Dictionary<string, string>() { { "tag1", "value1" }, { "tag2", "value2" } };

            // Once model caching is implemented, we should make the models slightly different to make sure the cache won't return copies of the same model.
            await using var disposableModel0 = await BuildDisposableDocumentModelAsync(TestEnvironment.BlobContainerSasUrl, description, tags);
            await using var disposableModel1 = await BuildDisposableDocumentModelAsync(TestEnvironment.BlobContainerSasUrl, description, tags);

            DocumentIntelligenceOperationDetails expectedOperation0 = await client.GetOperationAsync(disposableModel0.Operation.Id);
            DocumentIntelligenceOperationDetails expectedOperation1 = await client.GetOperationAsync(disposableModel1.Operation.Id);

            var expectedIdMapping = new Dictionary<string, DocumentIntelligenceOperationDetails>()
            {
                { disposableModel0.Operation.Id, expectedOperation0 },
                { disposableModel1.Operation.Id, expectedOperation1 }
            };
            var idMapping = new Dictionary<string, DocumentIntelligenceOperationDetails>();

            await foreach (DocumentIntelligenceOperationDetails operation in client.GetOperationsAsync())
            {
                if (expectedIdMapping.ContainsKey(operation.OperationId))
                {
                    idMapping.Add(operation.OperationId, operation);
                }

                if (idMapping.Count == expectedIdMapping.Count)
                {
                    break;
                }
            }

            Assert.That(idMapping.Count, Is.EqualTo(expectedIdMapping.Count));

            foreach (string id in expectedIdMapping.Keys)
            {
                Assert.That(idMapping, Contains.Key(id));

                DocumentIntelligenceOperationDetails expected = expectedIdMapping[id];
                DocumentIntelligenceOperationDetails operation = idMapping[id];

                Assert.That(operation.OperationId, Is.EqualTo(expected.OperationId));
                Assert.That(operation.ApiVersion, Is.EqualTo(expected.ApiVersion));
                Assert.That(operation.Status, Is.EqualTo(expected.Status));
                Assert.That(operation.PercentCompleted, Is.EqualTo(expected.PercentCompleted));
                Assert.That(operation.ResourceLocation.AbsoluteUri, Is.EqualTo(expected.ResourceLocation.AbsoluteUri));
                Assert.That(operation.CreatedOn, Is.EqualTo(expected.CreatedOn));
                Assert.That(operation.LastUpdatedOn, Is.EqualTo(expected.LastUpdatedOn));
                Assert.That(operation.Tags, Is.EquivalentTo(expected.Tags));

                Assert.That(operation.Error, Is.Null);
            }
        }

        #endregion List Operations

        private void ValidateOperationDetails(DocumentIntelligenceOperationDetails operationDetails, string operationId, string resourceLocation, DateTimeOffset startTime, IDictionary<string, string> tags)
        {
            Assert.That(operationDetails.OperationId, Is.EqualTo(operationId));
            Assert.That(operationDetails.ApiVersion, Is.EqualTo(ServiceVersionString));
            Assert.That(operationDetails.Status, Is.EqualTo(DocumentIntelligenceOperationStatus.Succeeded));
            Assert.That(operationDetails.PercentCompleted, Is.EqualTo(100));
            Assert.That(operationDetails.ResourceLocation.AbsoluteUri, Is.EqualTo(resourceLocation));

            Assert.That(operationDetails.Error, Is.Null);

            // Add a 4-hour tolerance because the model could have been cached before this test.
            Assert.That(operationDetails.CreatedOn, Is.GreaterThan(startTime - TimeSpan.FromHours(4)));
            Assert.That(operationDetails.LastUpdatedOn, Is.GreaterThan(operationDetails.CreatedOn));
            Assert.That(operationDetails.Tags, Is.EquivalentTo(tags));
        }
    }
}
